package com.haiqiu.security.filter;


import com.haiqiu.common.result.Constants;
import com.haiqiu.common.utils.web.ResponseUtil;
import com.haiqiu.common.result.ResultCode;
import com.haiqiu.common.result.ResultData;
import com.haiqiu.common.utils.web.JwtTokenUtil;
import com.haiqiu.common.utils.web.RedisUtil;
import lombok.SneakyThrows;
import com.haiqiu.security.entity.SecurityUser;
import com.haiqiu.system.entity.SysConfig;
import com.haiqiu.system.service.SysConfigService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.security.authentication.UsernamePasswordAuthenticationToken;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.security.core.userdetails.UserDetailsService;
import org.springframework.security.web.authentication.WebAuthenticationDetailsSource;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;
import org.springframework.web.filter.OncePerRequestFilter;

import javax.annotation.Resource;
import javax.servlet.FilterChain;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;

/**
 * @author HaiQiu
 * @date 2021/4/4 15:54
 * @desc token过滤验证器
 **/
@Component
public class JwtAuthenticationTokenFilter extends OncePerRequestFilter {

    @Resource
    private UserDetailsService userDetailsService;

    @Autowired
    private SysConfigService sysConfigService;

    @Autowired
    private JwtTokenUtil jwtTokenUtil;

    @Value("${jwt.authorization}")
    private String authorization;

    @Value("${jwt.header}")
    private String header;

    @Autowired
    private RedisUtil redisUtil;


    @Override
    protected void doFilterInternal(HttpServletRequest httpServletRequest,
                                    HttpServletResponse httpServletResponse,
                                    FilterChain filterChain) throws ServletException, IOException {
        //获取请求头token
        String authToken = httpServletRequest.getHeader(this.authorization);
        //token不为空
        if (!StringUtils.isEmpty(authToken)) {
            //截取 bearer 后面的字符串  并且 两端去空格（获取token）
            String token = authToken.substring(this.header.length()).trim();
            //校验token返回用户名
            String username = this.validateTokenData(token, httpServletResponse);
            //检验token获取用户名
            if (!StringUtils.isEmpty(username)) {
                SecurityUser securityUser = null;
                //获取缓存账户
                if (redisUtil.hasKey(Constants.REDIS_USER + username)) {
                    securityUser = (SecurityUser) redisUtil.get(Constants.REDIS_USER + username);
                    //否则从数据库获取数据账户
                } else {
                    securityUser = (SecurityUser) userDetailsService.loadUserByUsername(username);
                }
                UsernamePasswordAuthenticationToken authenticationToken = new UsernamePasswordAuthenticationToken(
                        securityUser, null, securityUser.getAuthorities());

                authenticationToken.setDetails(new WebAuthenticationDetailsSource().buildDetails(httpServletRequest));
                SecurityContextHolder.getContext().setAuthentication(authenticationToken);
            }
        }
        filterChain.doFilter(httpServletRequest, httpServletResponse);
    }

    /**
     * 校验token返回用户名
     */
    @SneakyThrows
    public String validateTokenData(String token, HttpServletResponse response) {
        //检验token和用户名
        String username = jwtTokenUtil.getUsername(token);
        String tokenFromRedis = null;
        //判断是否为多端登录，是则 保持redis多个key，否则唯一key
        if (isManyLogin()) {
            tokenFromRedis = (String) redisUtil.get(Constants.USER_TOKEN + username + "_" + token);

        } else {
            tokenFromRedis = (String) redisUtil.get(Constants.USER_TOKEN + username);
        }
        if (tokenFromRedis == null) {
            ResponseUtil.out(response, ResultData.fail(ResultCode.USER_ACCOUNT_EXPIRED));
            return null;
            //比较缓存里的token是否一致
        } else if (!tokenFromRedis.equals(token)) {
            ResponseUtil.out(response, ResultData.fail(ResultCode.USER_ACCOUNT_FAIL_LOGIN));
            return null;
        }
        return username;
    }


    /**
     * 判断是否为单端登录
     *
     * @return true是，false否
     */
    private Boolean isManyLogin() {
        SysConfig sysConfig = null;
        if (redisUtil.hasKey(Constants.CONFIG)) {
            sysConfig = (SysConfig) redisUtil.get(Constants.CONFIG);
        } else {
            sysConfig = sysConfigService.get(Constants.CONFIG_ID);
            redisUtil.set(Constants.CONFIG, sysConfig);
        }
        if (sysConfig.getManyLogin() == null) {
            sysConfig.setManyLogin(Constants.OFF);
        }
        return sysConfig.getManyLogin();
    }
}
